﻿using Dapper;
using Kele.Repository.Caching;
using Kele.Repository.Utils;
using SqlKata;
using SqlKata.Compilers;
using SqlKata.Execution;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Kele.Repository
{
    public partial class Repository
    {
        public QFactory QF => new QFactory(Database);

        public async Task<IEnumerable<T>> GetAsync<T>(Query query, string cacheKey = "")
        {
            using var qf = new QFactory(Database);

            var x = new XQuery(Database.GetConnection(), new MySqlCompiler());

            var compileInfo = await CachingUtils.CompileAsync(Redis, query);

            if (compileInfo?.Caching != null && compileInfo?.Caching.Action == CachingAction.Get)
            {
                var cacheManager = await CachingUtils.GetCacheManagerAsync(Redis);
                return await cacheManager.GetOrCreateAsync(compileInfo.Caching.CacheKey,
                    async () => await qf.DB.GetAsync<T>(query)
                );
            }

            return await qf.DB.GetAsync<T>(query);
        }

        public async Task<T> FirstOrDefaultAsync<T>(Query query, bool isCache = true)
        {
            using var qf = QF;
            if (isCache)
            {
                var from = query.GetOneComponent<FromClause>("from");
                var cacheKey = CacheUtils.GetEntityKey(from.Table);

                if (!string.IsNullOrEmpty(cacheKey))
                {
                    var cacheManager = await CachingUtils.GetCacheManagerAsync(Redis);
                    return await cacheManager.GetOrCreateAsync(cacheKey,
                        async () => await qf.DB.FirstOrDefaultAsync<T>(query)
                    );
                }
            }

            return await qf.DB.FirstOrDefaultAsync<T>(query);
        }

        #region free statements

        public async Task<IEnumerable<T>> SelectAsync<T>(string sql, object param = null)
        {
            using var connection = Database.GetConnection();
            return await connection.QueryAsync<T>(sql, param);
        }

        public async Task<IEnumerable<dynamic>> SelectAsync(string sql, object param = null)
        {
            using var connection = Database.GetConnection();
            return await connection.QueryAsync<dynamic>(sql, param);
        }

        public async Task<int> StatementAsync(string sql, object param = null)
        {
            using var connection = Database.GetConnection();
            return await connection.ExecuteAsync(sql, param);
        }

        #endregion free statements
    }
}